package cn.wizzer.app.user.modules.service.impl;

import cn.wizzer.app.crown.modules.models.constant.CrownConstant;
import cn.wizzer.app.match.modules.models.constant.Fields;
import cn.wizzer.app.match.modules.models.constant.RedisKey;
import cn.wizzer.app.match.modules.models.vo.MatchVO;
import cn.wizzer.app.match.modules.services.MatchService;
import cn.wizzer.app.sys.modules.models.SysParam;
import cn.wizzer.app.sys.modules.services.SysDictService;
import cn.wizzer.app.sys.modules.services.SysParamService;
import cn.wizzer.app.user.modules.models.User;
import cn.wizzer.app.user.modules.models.vo.LiveInfo;
import cn.wizzer.app.user.modules.models.vo.MatchLives;
import cn.wizzer.app.user.modules.services.LiveBroadcastService;
import cn.wizzer.app.user.modules.services.UserService;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import cn.wizzer.framework.base.service.CommonDBService;
import cn.wizzer.framework.page.Pagination;
import cn.wizzer.framework.util.JSONUtil;
import cn.wizzer.util.VideoUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.http.Http;
import org.nutz.http.Response;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.wizzer.app.match.modules.models.constant.Fields.*;


@SuppressWarnings("AlibabaUndefineMagicConstant")
@IocBean(args = {"refer:dao"})
@Service(interfaceClass = MatchService.class)
public class MatchServiceImpl extends BaseServiceImpl<MatchVO> implements MatchService {

    public MatchServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private CommonDBService dbService;
    private static final Log log = Logs.get();
    @Inject
    private RedisService redisService;

    @Inject
    private CommonDBService commonDBService;

    @Inject
    @Reference
    private UserService userService;

    @Reference
    @Inject
    SysDictService sysDictService;

    @Reference
    @Inject
    SysParamService sysParamService;

    @Inject
    @Reference
    private LiveBroadcastService liveBroadcastService;
    @Inject
    private PropertiesProxy conf;

    @Override
    public void sync() {
        // 官方账号
        User user = userService.fetch(Cnd.where("phone", "=", PHONE_NUM));
        JSONArray array = getData();
        if (array.size() == 0) {
            return;
        }
        Map<String, String> oldMatchMap = redisService.hgetAll(RedisKey.KEY_MATCH);
        Map<String, String> matchInfo = new HashMap<>();
        for (int i = 0; i < array.size(); i++) {
            MatchVO data = parseToMatch(array.getJSONObject(i));
            String oldMatchStr = oldMatchMap.get(data.getVideoId());
            MatchVO oldMatch = ((MatchLives) JSONUtil.parseStringToJavaBean(MatchLives.class, oldMatchStr)).getMatch();
            if (oldMatch != null) {
                //原标签
                data.setLabels(oldMatch.getLabels());
                //原状态
                data.setDelFlag(oldMatch.getDelFlag());
            }
            // 直播间编号
            String liveNo = user.getId() + _AND_ + data.getVideoId();
            // 赛事信息插入到redis
            if (data.getStatus().equals(CrownConstant.FUTURE)) {
                matchInfo.put(data.getVideoId(), JSON.toJSONString(new MatchLives(data)));
            }
            //直播中的源则加入到官方直播中
            else if (data.getStatus().equals(PLAYING) && Strings.isNotBlank(data.getStreamUrl())) {
                String streamUrl = data.getStreamUrl();
                //截取封面图
                String imgUri = _LIVE_ + liveNo + Fields.PNG_SUFFIX;
                String imgPath = conf.get(JETTY_STATIC_PATH, UPLOAD) + imgUri;
                boolean saveFile = VideoUtil.fetchFrame(streamUrl, imgPath);
                if (saveFile) {
                    if (new File(imgPath).exists()) {
                        //region 缓存官方直播数据
                        //可能为空但parse方案会返回一个属性为空的对象
                        LiveInfo oldLiveInfo = JSONUtil.parseStringToJavaBean(LiveInfo.class, redisService.hget(RedisKey.KEY_LIVE, liveNo));
                        //创建官方直播缓存VO
                        LiveInfo liveInfo = new LiveInfo(user.getId(), data.getLeague(), streamUrl,
                                user.getNickName(), user.getSign(), user.getHeadPicture(),
                                data.getCategory(), data.getVideoId(),
                                imgUri, oldLiveInfo.getLabels(),
                                oldLiveInfo.getUserCount() == null ? 0L : oldLiveInfo.getUserCount());
                        //主客队
                        liveInfo.setHomeName(data.getHomeName());
                        liveInfo.setAwayName(data.getAwayName());

                        //设置可见ID
                        String liveCode = data.getVideoId();

                        liveInfo.setLiveCode(liveCode);
                        //保留删除状态
                        liveInfo.setDelFlag(oldLiveInfo.isDelFlag());
                        //保留原设定盘口
                        liveInfo.setMatchId(oldLiveInfo.getMatchId());
                        //设置默认标题
                        liveInfo.setNotice("官方直播");
                        //设置默认标签
                        if (Strings.isNotBlank(oldLiveInfo.getLabels())) {
                            liveInfo.setLabels(oldLiveInfo.getLabels());
                        } else {
                            liveInfo.setLabels("官方");
                        }
                        //设置默认标题
                        liveInfo.setTitle(data.getLeague());
                        // redis中添加默认直播数据
                        redisService.hset(RedisKey.KEY_LIVE, liveNo, JSONObject.toJSONString(liveInfo));
                        //endregion
                        //region 缓存赛事数据
                        MatchLives matchLives = new MatchLives(data);
                        //原赛事数据
                        MatchLives oldMatchInfo = JSON.toJavaObject(JSON.parseObject(redisService.hget(RedisKey.KEY_MATCH, data.getVideoId())), MatchLives.class);
                        //如有在播主播则保留相关主播信息
                        if (oldMatchInfo != null && oldMatchInfo.getAnchors() != null && oldMatchInfo.getAnchors().size() > 0) {
                            matchLives.addAnchors(oldMatchInfo.getAnchors());
                        } else {
                            matchLives.addAnchors(liveInfo);
                        }
                        matchInfo.put(data.getVideoId(), JSONObject.toJSONString(matchLives));
                        //endregion
                    }
                }
            }
        }
        redisService.del(RedisKey.KEY_MATCH);
        matchInfo.forEach((key, val) -> redisService.hset(RedisKey.KEY_MATCH, key, val));
        redisService.expire(RedisKey.KEY_MATCH, RedisKey.TEN_MIN);
    }

    private JSONArray getData() {
        SysParam param = sysParamService.fetch();
        String url = "http://list.naliangkuai.com/live_list?type=basketball&key=" + param.getMatchKey();
        Response resp = Http.get(url, new HashMap<>(), 100000);
        JSONArray array = JSON.parseArray(resp.getContent());
        url = "http://list.naliangkuai.com/live_list?type=football&key=" + param.getMatchKey();
        resp = Http.get(url, new HashMap<>(), 100000);
        array.addAll(JSON.parseArray(resp.getContent()));
        url = "http://list.naliangkuai.com/live_list?type=dianjing&key=" + param.getMatchKey();
        resp = Http.get(url, new HashMap<>(), 100000);
        array.addAll(JSONUtil.parseToJSONArray(resp.getContent()));
        return array;
    }

    private MatchVO parseToMatch(JSONObject jsonObject) {
        Long matchtime = new Long(jsonObject.getString("Matchtime") + "000");
        MatchVO matchVO = new MatchVO();
        matchVO.setStreamState(jsonObject.getJSONObject("StreamInfo").getBoolean("state"));
        matchVO.setStatus(matchtime > System.currentTimeMillis() ? CrownConstant.FUTURE : "playing");
        matchVO.setScore(jsonObject.getString("Scores"));
        matchVO.setLeague(jsonObject.getString("Liga"));
        matchVO.setHomeName(jsonObject.getString("Opp1"));
        matchVO.setAwayName(jsonObject.getString("Opp2"));
        matchVO.setDatetime(new Date(matchtime));
        matchVO.setCategory(jsonObject.getString("Sport"));
        matchVO.setStreamUrl(jsonObject.getString("player_url"));
//        matchVO.setVideoId(jsonObject.getString("vid"));
        matchVO.setVideoId(jsonObject.getString("StreamName").replace("stream", ""));
        return matchVO;
    }

    @Override
    public Collection categoryList() {
        return sysDictService.getSortedSubsListByCode(MATCH_CATEGORY);
    }

    @Override
    public Pagination listByCode(String code, Long startTime, Long endTime, int pageNumber, int pageSize) {
        Pagination pagination = new Pagination();
        try {
            // redis中取出赛事信息
            Map<String, String> match = redisService.hgetAll(RedisKey.KEY_MATCH);
            Stream<MatchLives> stream = match.values().stream().map(this::parseToMatch);
            //  分类判断
            if (Strings.isNotBlank(code)) {
                //规则匹配
                if (code.startsWith(PREFIX)) {
                    try {
                        JSONArray rules = JSONUtil.parseToJSONArray(code);
                        for (Object obj : rules) {
                            JSONObject rule = (JSONObject) obj;
                            String property = rule.getString(FIELD_PROPERTY);
                            String expression = rule.getString(FIELD_EXPRESSION);
                            JSONArray values = rule.getJSONArray(FIELD_VALUES);
                            switch (expression) {
                                case FIELD_EXPRESSION_EQUALS: {
                                    stream = stream.filter(m -> {
                                        JSONObject jsonObject = JSONUtil.toJSON(m).getJSONObject(MATCH);
                                        if (jsonObject.containsKey(property)) {
                                            //若包含此属性则
                                            for (Object value : values) {
                                                if (jsonObject.getString(property).equals(value.toString())) {
                                                    return true;
                                                }
                                            }
                                            return false;
                                        }
                                        return true;
                                    });
                                    break;
                                }
                                case FIELD_EXPRESSION_NOT_EQUALS: {
                                    stream = stream.filter(m -> {
                                        JSONObject jsonObject = JSONUtil.toJSON(m).getJSONObject(MATCH);
                                        if (jsonObject.containsKey(property)) {
                                            for (Object value : values) {
                                                if (jsonObject.getString(property).equals(value.toString())) {
                                                    return false;
                                                }
                                            }
                                            return true;
                                        }
                                        return true;
                                    });
                                    break;
                                }
                                case FIELD_EXPRESSION_CONTAINS: {
                                    stream = stream.filter(m -> {
                                        JSONObject jsonObject = JSONUtil.toJSON(m).getJSONObject(MATCH);
                                        //若包含此属性则
                                        if (jsonObject.containsKey(property)) {
                                            for (Object value : values) {
                                                if (null != jsonObject.getString(property) && jsonObject.getString(property).contains(value.toString())) {
                                                    return true;
                                                }
                                            }
                                            return false;
                                        }
                                        return true;
                                    });
                                    break;
                                }
                                case FIELD_EXPRESSION_NOT_CONTAINS: {
                                    stream = stream.filter(m -> {
                                        JSONObject jsonObject = JSONUtil.toJSON(m).getJSONObject(MATCH);
                                        if (jsonObject.containsKey(property)) {
                                            //若包含此属性则
                                            for (Object value : values) {
                                                if (null != jsonObject.getString(property) && jsonObject.getString(property).contains(value.toString())) {
                                                    return false;
                                                }
                                            }
                                            return true;
                                        }
                                        return true;
                                    });
                                    break;
                                }
                                default: {
                                }
                            }
                        }
                    } catch (Exception e) {
                        log.error(e.getMessage());
                    }
                } else {
                    stream = stream.filter(m -> m.getMatch().getCategory().equals(code));
                }
            }
            // 时间判断
            if (null != startTime && null != endTime) {
                stream = stream.filter(m -> null != m.getMatch().getDatetime() && m.getMatch().getDatetime().getTime() >= startTime && m.getMatch().getDatetime().getTime() < endTime);
            } else if (null != startTime) {
                stream = stream.filter(m -> null != m.getMatch().getDatetime() && m.getMatch().getDatetime().getTime() >= startTime);
            } else if (null != endTime) {
                stream = stream.filter(m -> null != m.getMatch().getDatetime() && m.getMatch().getDatetime().getTime() < endTime);
            }
            //排序
            stream = stream.sorted(Comparator.comparingLong(MatchLives::getMatchDate));
            //分页
            List<MatchLives> ls = stream.collect(Collectors.toList());
            int count = ls.size();
            if (pageNumber == 0) {
                pageNumber = 1;
            }
            if (pageSize == 0) {
                pageSize = count;
            }
            int endIndex = pageNumber * pageSize;
            int startIndex = endIndex - pageSize;
            List<MatchLives> dataList = ls.subList(startIndex, Math.min(endIndex, count));

            pagination.setList(dataList);
            pagination.setTotalCount(count);
            pagination.setPageSize(pageSize);

            return pagination;
        } catch (Exception e) {
            return pagination;
        }
    }

    @Override
    public void updateByVideoId(MatchLives match) {
        redisService.hset(RedisKey.KEY_MATCH, match.getMatch().getVideoId(), JSONUtil.toJSONString(match));
    }

    @Override
    public MatchLives hget(String id) {
        return JSONUtil.parseStringToJavaBean(MatchLives.class, redisService.hget(RedisKey.KEY_MATCH, id));
    }

    private MatchLives parseToMatch(String str) {
        return ((JSONObject) JSON.parse(str)).toJavaObject(MatchLives.class);
    }

}
